#pragma once

#include "downsample.h"
#include "type_traits.h"
#include <algorithm>
#include <cassert>

namespace img {

class bitmap;


template <class IR, class OR>
void downsample(IR const & ir, OR & or, unsigned scale_x, unsigned scale_y)
{ 
	if (scale_x == 1 && scale_y == 1) {
		or = ir;
		return;
	}
	or.resize(ir.width() / scale_x, ir.height() / scale_y);
	typedef typename IR::elem_type color;
	typedef typename xtd::type_traits<color>::accumulator_type accu_color;

	///float multiplier = 1.0f / (scale_x * scale_y); ///ideal for float IR, but not don't work for int
	unsigned block_size = scale_x * scale_y; ///bad for int-to-float downsampling
	typename OR::iterator out;
	for (out = or.begin(); out != or.end(); ++out) {
		accu_color sum = accu_color(); //it's assumed that its initialized with zeros
		unsigned base_y = out.y() * scale_y;
		for (unsigned y = base_y; y < base_y + scale_y; ++y) {
			unsigned base_x = out.x() * scale_x;
			for (unsigned x = base_x; x < base_x + scale_x; ++x)
				sum += ir(x, y);
		}
		*out = sum / block_size;
	}
}

/* this version of downsample produces larger result for dimenstions not divisible on scales
template <class IR, class OR>
void downsample(IR const & ir, OR & or, unsigned scale_x, unsigned scale_y)
{ 
	or.resize((ir.width() + scale_x - 1) / scale_x, (ir.height() + scale_y - 1) / scale_y);
	typedef typename IR::elem_type color;
	typedef typename xtd::type_traits<color>::accumulator_type accu_color;

	typename OR::iterator out;
	for (out = or.begin(); out != or.end(); ++out) {
		accu_color sum = accu_color(); //it's assumed that its initialized with zeros
		unsigned pixels = 0;
		unsigned base_y = out.y() * scale_y;
		unsigned end_y = std::min(base_y + scale_y, ir.height());
		for (unsigned y = base_y; y < end_y; ++y) {
			unsigned base_x = out.x() * scale_x;
			unsigned end_x = std::min(base_x + scale_x, ir.width());
			for (unsigned x = base_x; x < end_x; ++x) {
				sum += ir(x, y);
				++pixels;
			}
		}
		assert (pixels > 0);
		*out = sum / pixels;
	}
}*/

template <>
void downsample(bitmap const & ir, bitmap & or, unsigned scale_x, unsigned scale_y);

///

template <class IR, class OR>
void upsample(IR const & ir, OR & or, unsigned scale_x, unsigned scale_y)
{ 
	assert(or.width() >= scale_x * ir.width());
	assert(or.height() >= scale_y * ir.height());

	typename IR::const_iterator in;
	for (in = ir.begin(); in != ir.end(); ++in) {
		unsigned base_y = in.y() * scale_y;
		for (unsigned y = base_y; y < base_y + scale_y; ++y) {
			unsigned base_x = in.x() * scale_x;
			for (unsigned x = base_x; x < base_x + scale_x; ++x)
				or(x, y) = *in;
		}
	}
}

} //namespace img
